package assist.datastore;

import java.util.Collection;
import java.util.logging.Logger;

import javax.jdo.JDOHelper;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;


public class JDODatastore<T> implements Datastore<T>
{
    private static final Logger log = Logger.getLogger(JDODatastore.class.getName());
  /**
   * Singleton instance of the Factory.  Note, it is not marked final because
   * it CAN be reset for testing.  
   * @see reset() method below.
   */
  private static PersistenceManagerFactory PMF = 
      JDOHelper.getPersistenceManagerFactory("google-cloud-sql");
      //JDOHelper.getPersistenceManagerFactory("mysql-local");
      //JDOHelper.getPersistenceManagerFactory("appengine-datastore");
 // static
 // {
 //     
 // }
   //   

  /**
   * Resets the PersistenceManagerFactory.
   *  SHOULD ONLY BE CALLED FROM TESTING ENVIRONMENTS.  Requires the 
   *  appengine.orm.disable.duplicate.pmf.exception System property
   *  to be set to false.  If this is called in a runtime environment without
   *  setting the property an exception will be thrown.
   */
  public static void reset()
  {
      PMF =
     //     JDOHelper.getPersistenceManagerFactory("transactions-optional");
          JDOHelper.getPersistenceManagerFactory("google-cloud-sql");
      
  }
  
  /**
   * Grabs a PersistenceManager from the PMF.  This is used internally, but
   * can also be used in the application layer for more complex transactions.
   * @return A PersistenceManager.
   */
  public static PersistenceManager getPersistenceManager()
  {
     
      return  PMF.getPersistenceManager();
  }
  
  public static PersistenceManagerFactory getPMF()
  {
      return PMF;
  }
  
  /**
   * Convenience method that gets a PersistenceManager, calls makePersistent
   * on it to persist the given object by creating or updating it in the 
   * datastore, and then closes the PersistenceManager.
   * @param object the object to persist to the datastore.
   * @param <T> The type of object that is persisted.
   */  
  public T save(final T object)
  {
      final PersistenceManager pm = JDODatastore.getPersistenceManager();     
      try 
      {              
          return pm.makePersistent(object);
      } 
      finally 
      {  
          pm.close();  
      }                 
  }
  
  /**
   * Convenience method that gets a PersistenceManager, calls makePersistentAll
   * on it to persist the given object collection and then closes the 
   * PersistenceManager.
   * @param object the object to persist to the datastore.
   * @param <T> The type of object that is persisted.
   */
  
  public Collection<T> saveAll(final Collection<T> objects)
  {
      final PersistenceManager pm = JDODatastore.getPersistenceManager();     
      try 
      {              
          log.info("Saving..." + objects.size());
          Collection<T> saved = pm.makePersistentAll(objects);
          log.info("Saved..." + saved.size());
          
       //   pm.getN
          return saved;
      } 
   /*   catch (NucleusDataStoreException e)
      {
          Exception e = e.get
      }*/
      finally 
      {  
          pm.close();  
      }  
  }  

  /**
   * Retrieves an object from the datastore with the given type and id.
   * NOTE: This retrieves entire object. This could be very resource consuming
   * for large objects.  
   * For objects with large child collections, do not use this method, use the
   * PersistenceManager directly and take advantage of the lazy loading, etc. 
   * @param <T> The type of object to retrieve.
   * @param clazz The class of the object to retrieve.
   * @param id The ID of the object to retrieve.
   * @return The object found in the datastore that matches the type and ID.
   */
  public T getById(final Class<T> clazz, final Object id)
  {
      final PersistenceManager pm = PMF.getPersistenceManager();
      T object;
      try 
      {  
          //It seems that this should be required to ensure that all fields
          //and subfileds are retrieved, however it works without it.
          //pm.getFetchPlan().setMaxFetchDepth(-1);
          
          object = pm.getObjectById(clazz, id);  
          
          //this populates all fields in the object, which could be resource
          //consuming, but if this is not done here, the collections are left
          //null when the pm is closed below.
          //NOTE: Fields that are nested more than one level need to set the
          //@Persistent(defaultFetchGroup="true") in their annotation.                        
          pm.retrieve(object);
          return pm.detachCopy(object);
      }
      catch (JDOObjectNotFoundException e)
      {
          return null;
      }
      finally 
      {  
          pm.close();  
      } 
      
  }        
}

